05 网络与存储
容器默认是相互隔离的——你的Agent容器访问不了Redis容器,因为它们在不同的"房间"里。另外,容器被删除后里面的数据就没了——你的对话记录、数据库文件全都会消失。
这两个问题分别由Docker网络和Docker存储来解决。
一、端口映射
容器有自己独立的网络命名空间,默认和外部隔离。要从宿主机访问容器内的服务,需要做端口映射。
1.1 基本用法
# 宿主机8080端口 → 容器80端口
docker run -d -p 8080:80 nginx格式是-p 宿主机端口:容器端口。访问http://localhost:8080就会被转发到容器的80端口。
1.2 EXPOSE vs 端口映射
Dockerfile里的EXPOSE只是声明,不会实际发布端口。要真正从外部访问,必须在docker run时用-p映射,或者在compose.yaml里配置ports。
# compose.yaml
services:
web:
ports:
- "8000:5000"1.3 端口映射注意事项
映射端口时要注意:
- 一个宿主机端口只能映射给一个容器
- 不指定宿主机端口让Docker自动分配:
-p 80(容器80端口映射到宿主机的随机端口) - 绑定到特定网卡:
-p 127.0.0.1:8080:80(只允许本机访问)
安全提醒: 端口映射默认绑定到所有网卡(0.0.0.0),意味着任何能访问你机器的人都能访问这个端口。数据库这类敏感服务不要暴露端口,或者绑定到127.0.0.1。
二、容器网络
2.1 默认网络
Docker安装后会创建一个默认的bridge网络。用docker run启动的容器都连接到这个网络。
# 查看Docker网络列表
docker network ls输出类似:
NETWORK ID NAME DRIVER SCOPE
a1b2c3d4e5f6 bridge bridge local
f6e5d4c3b2a1 host host local
1a2b3c4d5e6f none null local三种内置网络:
| 网络 | 说明 |
|---|---|
| bridge | 默认网络,容器之间通过虚拟网桥通信 |
| host | 容器直接使用宿主机的网络栈,没有隔离 |
| none | 没有网络,容器完全断网 |
2.2 容器间通信
在同一个bridge网络里,容器可以用容器名作为主机名互相访问。
# 创建自定义网络
docker network create my-network
# 启动Redis,加入my-network
docker run -d --name redis --network my-network redis:alpine
# 启动Agent服务,加入my-network
docker run -d --name agent --network my-network my-agent:1.0在Agent容器里,可以直接用redis作为主机名连接Redis:
import redis
r = redis.Redis(host='redis', port=6379)用Compose时不需要手动创建网络。 Compose会自动创建一个默认网络,所有服务都加入这个网络,服务名就是主机名。
2.3 网络别名
一个容器可以有多个网络别名:
docker run -d --network my-network --network-alias db --network-alias database postgres其他容器可以用db或database访问这个容器。
三、数据持久化
容器被删除后,里面的数据就没了。如果你在容器里跑了一个Redis,存了一些数据,docker rm之后数据全部消失。
Docker提供两种数据持久化方案:Volume(数据卷)和Bind Mount(绑定挂载)。
3.1 Volume(数据卷)
Volume是Docker管理的存储区域,独立于容器的生命周期。容器删了,Volume还在。
# 创建Volume
docker volume create my-data
# 启动容器并挂载Volume
docker run -d -v my-data:/data redis:alpine-v my-data:/data的意思是把my-data这个Volume挂载到容器的/data目录。容器往/data写的数据实际存在Volume里。
验证数据持久化:
# 启动Redis并写入数据
docker run -d --name redis1 -v redis-data:/data redis:alpine
docker exec redis1 redis-cli SET mykey "hello"
# 删除容器
docker rm -f redis1
# 用同一个Volume启动新容器
docker run -d --name redis2 -v redis-data:/data redis:alpine
docker exec redis2 redis-cli GET mykey
# 输出: "hello"数据还在,因为Volume没有随容器删除。
3.2 Volume管理
# 列出所有Volume
docker volume ls
# 查看Volume详情
docker volume inspect my-data
# 删除指定Volume
docker volume rm my-data
# 删除所有未使用的Volume
docker volume prune在Compose里使用Volume:
services:
redis:
image: redis:alpine
volumes:
- redis-data:/data
# 声明Volume
volumes:
redis-data:docker compose down不会删除Volume,数据会保留。docker compose down -v会连Volume一起删除。
3.3 Bind Mount(绑定挂载)
Bind Mount把宿主机的目录直接挂载到容器里。和Volume不同,Bind Mount的文件直接存在宿主机上,你可以直接用编辑器修改。
# 把当前目录挂载到容器的/app
docker run -d -v $(pwd):/app my-app:1.0Bind Mount适合开发场景——你改代码,容器里立刻生效。但不适合生产环境,因为依赖宿主机的目录结构。
在Compose里使用Bind Mount:
services:
web:
build: .
volumes:
- ./src:/app/src # 绑定挂载
- redis-data:/data # 命名卷3.4 Volume vs Bind Mount
| 对比项 | Volume | Bind Mount |
|---|---|---|
| 管理方 | Docker管理 | 用户管理 |
| 存储位置 | Docker数据目录 | 宿主机任意目录 |
| 可移植性 | 高(不依赖宿主机路径) | 低(依赖宿主机路径) |
| 适合场景 | 数据持久化(数据库、日志) | 开发时挂载代码 |
| 性能 | 好 | Linux上好,Mac/Windows上有性能问题 |
生产环境用Volume,开发环境用Bind Mount。
四、网络和存储的组合
来看一个实际的Agent服务架构:
services:
web:
build: .
ports:
- "8000:5000"
environment:
- REDIS_HOST=redis
volumes:
- app-logs:/app/logs
depends_on:
redis:
condition: service_healthy
redis:
image: redis:alpine
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
volumes:
redis-data:
app-logs:这个架构:
- web和redis在同一个网络里,web用
redis主机名访问redis - web的5000端口映射到宿主机的8000端口
- redis的数据持久化到
redis-data卷 - web的日志持久化到
app-logs卷 - 健康检查保证redis就绪后再启动web
五、总结
| 概念 | 作用 |
|---|---|
端口映射(-p) | 从宿主机访问容器服务 |
| bridge网络 | 默认网络,容器间用服务名通信 |
| Volume | Docker管理的数据持久化,适合生产 |
| Bind Mount | 宿主机目录直接挂载,适合开发 |
depends_on + healthcheck | 控制启动顺序,保证依赖就绪 |
核心原则:
- 端口映射要谨慎——敏感服务不要暴露,或者绑定到
127.0.0.1 - 数据要持久化——数据库、日志等数据必须用Volume,不然容器删了数据就没了
- 生产用Volume,开发用Bind Mount
下一篇是实战篇——把前面学到的所有知识串起来,部署一个完整的AI Agent服务。